JavaFX 8 からは印刷用のパッケージ(javafx.print)が追加されている。これによりJavaFX上で画面内容の出力や、利用可能な印刷機器一覧や印刷ダイアログの表示も可能になった。また出力する内容はシーングラフであればよいため、例えばScene Builder等で作成したFXMLレイアウトに対してJavaFXで値の代入・印刷を行うような帳票出力プログラムも簡単に作成できる。別記事で紹介した
バーコードライブラリを利用すれば在庫管理や簡易レジアプリも楽に作成でき、応用範囲は広いと思われる。
ということで、今回はJavaFXで印刷する方法について確認する。
■ 画面印刷
まずは画面印刷を行うサンプル・プログラムにより、JavaFXでの印刷方法について確認を行う。サンプルでは印刷ボタンを押下することでページ設定ダイアログ・印刷ダイアログが表示され、その後画面の印刷が始まる。
◇リソース
(ean13.png)
◇サンプルコード
package application;
import java.io.File;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.print.PrinterJob;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TestPrint1 extends Application
{
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage primaryStage)
{
// フォント色がおかしくなることへの対処
System.setProperty( "prism.lcdtext" , "false" );
// シーングラフの作成
VBox root = new VBox();
Label label = new Label( "印刷テスト" );
Image img = new Image( new File("img/ean13.png").toURI().toString() );
ImageView view = new ImageView( img );
Button button = new Button( "印刷" );
root.getChildren().addAll( label , view , button );
// シーンの作成
Scene scene = new Scene( root , 250 , 200 );
// ウィンドウ表示
primaryStage.setScene( scene );
primaryStage.show();
// 印刷ボタンにイベントハンドラを設定
button.addEventFilter( ActionEvent.ACTION , e ->
{
// 印刷タスクを定義
Task<Boolean> printTask = new Task<Boolean>()
{
@Override
public Boolean call()
{
// 印刷ジョブの作成
PrinterJob job = PrinterJob.createPrinterJob();
if( job == null ){ return false; }
// ジョブ状態を標準出力
job.jobStatusProperty().addListener( e -> System.out.println( "status : " + e ) );
// 印刷ダイアログの表示
if( !job.showPrintDialog(primaryStage) ){ return false; }
// ページ設定ダイアログの表示
if( !job.showPageSetupDialog(primaryStage) ){ return false; }
// シーングラフを印刷
if( !job.printPage( root ) ){ return false; }
// 印刷ジョブの終了
return job.endJob();
}
};
// 別スレッドで印刷を開始
Thread thread = new Thread( printTask );
thread.setDaemon( true );
thread.start();
});
}
}
◇実行結果
(出力画面)
|
(ボタン押下で表示される印刷ダイアログ)
|
(ボタン押下で表示されるページ設定ダイアログ)
|
(印刷結果。プリンタとしてPDF出力を選択)
|
◇解説
JavaDocによると印刷は専用のタスクを作成したほうがいいとのこと(
*2)なので、印刷用のタスクを定義している(49行目~74行目)。タスク内ではPrinterJob::createPrinterJob関数で印刷ジョブを作成(55行目)、印刷ダイアログの表示(62行目)とページ設定ダイアログの表示(65行目)を行い印刷準備を行っている。その後、印刷対象となるシーングラフとして「root」を指定して、PrinterJob::printPage関数で印刷を開始(68行目)、PrinterJob::endJob関数で印刷を完了している(71行目)。もし、画面全体でなくバーコードのみを表示したいなどの場合には、該当のシーングラフをPrinterJob::printPage関数に渡せばよい。
■ 設定・取得可能な印刷情報
上記のサンプル・プログラムではページ設定ダイアログ・印刷ダイアログを表示して印刷情報を設定していたが、ダイアログを出力せずにプログラム上で印刷情報を決定することも可能である。設定可能な印刷情報は以下のとおりである。表の見方としては、階層2は階層1のプロパティとして設定可能であり、階層3は階層2のプロパティとして設定可能であることを示している。
階層1 |
階層2 |
階層3 |
Printer
(プリンター。PrinterJob::getPrinter関数等で取得) |
PageLayout
(ページレイアウト。Printer::createPageLayout関数で作成) |
Paper
(用紙サイズ) |
PageOrientation
(用紙向き) |
Printer.MarginType
(マージン) |
JobSettings
(ジョブ設定。Printer::getJobSettings関数で取得) |
Name
(ジョブ名) |
Collation
(丁合) |
Copies
(印刷部数) |
PageaRange
(印刷ページ指定) |
PaperSource
(給紙方法) |
PrintColor
(カラー設定) |
PrintQuality
(印刷品質) |
PrintResolution
(印刷解像度) |
PrintSides
(両面印刷設定) |
以下に指定可能な印刷情報を取得するサンプルプログラムを示す。サンプルでは、利用可能なすべてのプリンター機器に対して設定可能な印刷情報の一覧を表示している。
◇サンプルコード
package application;
import javafx.application.Application;
import javafx.collections.ObservableSet;
import javafx.print.Collation;
import javafx.print.PageOrientation;
import javafx.print.Paper;
import javafx.print.PaperSource;
import javafx.print.PrintColor;
import javafx.print.PrintQuality;
import javafx.print.PrintResolution;
import javafx.print.PrintSides;
import javafx.print.Printer;
import javafx.print.PrinterAttributes;
import javafx.stage.Stage;
public class TestPrint2 extends Application
{
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage primaryStage)
{
// プリンター一覧を出力
ObservableSet<Printer> printers = Printer.getAllPrinters();
for( Printer printer : printers )
{
// プリンタ情報を出力
String def = ( printer == Printer.getDefaultPrinter() )? "default - " : "";
String name = printer.getName();
PrinterAttributes attr = printer.getPrinterAttributes();
// 名前
System.out.println( String.format( "%s%s" , def , name ) );
// 丁合
System.out.println( String.format( "◆利用可能な丁合:(デフォルト%s)" , attr.getDefaultCollation() ) );
for( Collation c : attr.getSupportedCollations() )
{ System.out.println( String.format(" %s", c ) ); }
// 印刷部数
System.out.println( String.format( "◆印刷部数:(デフォルト%s)" , attr.getDefaultCopies() ) );
System.out.println( String.format( " 最大印刷部数:%s" , attr.getMaxCopies() ) );
// 用紙向き
System.out.println( String.format( "◆利用可能な用紙向き:(デフォルト%s)" , attr.getDefaultPageOrientation() ) );
for( PageOrientation o : attr.getSupportedPageOrientations() )
{ System.out.println( String.format(" %s", o ) ); }
// 用紙サイズ
System.out.println( String.format( "◆利用可能な用紙サイズ:(デフォルト%s)" , attr.getDefaultPaper() ) );
for( Paper p : attr.getSupportedPapers() )
{ System.out.println( String.format(" %s", p ) ); }
// 給紙方法
System.out.println( String.format( "◆利用可能な給紙方法:(デフォルト%s)" , attr.getDefaultPaperSource() ) );
for( PaperSource p : attr.getSupportedPaperSources() )
{ System.out.println( String.format(" %s", p.getName() ) ); }
// 色設定
System.out.println( String.format( "◆利用可能な色設定:(デフォルト%s)" , attr.getDefaultPrintColor() ) );
for( PrintColor p : attr.getSupportedPrintColors() )
{ System.out.println( String.format(" %s", p ) ); }
// 品質設定
System.out.println( String.format( "◆利用可能な品質設定:(デフォルト%s)" , attr.getDefaultPrintQuality() ) );
for( PrintQuality p : attr.getSupportedPrintQuality() )
{ System.out.println( String.format(" %s", p ) ); }
// 印刷解像度
System.out.println( String.format( "◆利用可能な印刷解像度:(デフォルト%s)" , attr.getDefaultPrintResolution() ) );
for( PrintResolution p : attr.getSupportedPrintResolutions() )
{ System.out.println( String.format(" %s", p ) ); }
// 両面設定
System.out.println( String.format( "◆利用可能な両面印刷:(デフォルト%s)" , attr.getDefaultPrintSides() ) );
for( PrintSides p : attr.getSupportedPrintSides() )
{ System.out.println( String.format(" %s", p ) ); }
// ページ範囲サポート
System.out.println( attr.supportsPageRanges() ? "ページ範囲サポートあり" : "ページ範囲サポートなし" );
System.out.println( "" );
}
}
}
◇実行結果
default - CubePDF
◆利用可能な丁合:(デフォルトCOLLATED)
UNCOLLATED
COLLATED
◆印刷部数:(デフォルト1)
最大印刷部数:999
◆利用可能な用紙向き:(デフォルトPORTRAIT)
PORTRAIT
LANDSCAPE
REVERSE_LANDSCAPE
◆利用可能な用紙サイズ:(デフォルトPaper: A4 size=210.0x297.0 MM)
Paper: 8x10 size=8.0x10.0 INCH
Paper: A0 size=841.0x1189.0 MM
Paper: A1 size=594.0x841.0 MM
Paper: A2 size=420.0x594.0 MM
Paper: A3 size=297.0x420.0 MM
…
■ FXMLファイルの印刷
以下にFXMLファイルからレイアウトを取得して印刷するサンプルプログラムを示す。サンプルでは、FXMLで作成した帳票に対してJavaFX上で値を代入・印刷している。
◇リソース
TestPrintReport.fxml
◇サンプルコード
package application;
import java.net.URL;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.print.PageLayout;
import javafx.print.PageOrientation;
import javafx.print.Paper;
import javafx.print.Printer;
import javafx.print.PrinterJob;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class TestPrint3 extends Application
{
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage primaryStage)
{
// フォント色がおかしくなることへの対処
System.setProperty( "prism.lcdtext" , "false" );
// シーングラフの作成
VBox root = new VBox();
Label label = new Label( "帳票印刷プログラム" );
TextField name = new TextField();
name.setPromptText( "出力する名前を入力してください。" );
TextField money = new TextField();
money.setPromptText( "出力する金額を入力してください。" );
Button button = new Button( "印刷" );
root.getChildren().addAll( label , name , money , button );
// シーンの作成
Scene scene = new Scene( root , 250 , 200 );
// ウィンドウ表示
primaryStage.setScene( scene );
primaryStage.show();
// 印刷ボタンにイベントハンドラを設定
button.addEventFilter( ActionEvent.ACTION , e ->
{
// FXMLの読込設定
URL location = getClass().getResource( "TestPrintReport.fxml" );
FXMLLoader fxmlLoader = new FXMLLoader( location );
// FXMLファイルの読込
Pane report = null;
Stage reportWin = null;
try
{
// FXMLの読込
report = (Pane) fxmlLoader.load();
// 値の設定
TestPrint3ReportController controller;
controller = fxmlLoader.getController();
controller.setName( name.getText() );
controller.setMoney( money.getText() );
// ウィンドウの表示
Scene reportScene = new Scene( report , report.getPrefWidth() , report.getPrefHeight() );
reportWin = new Stage();
reportWin.initModality( Modality.APPLICATION_MODAL );
reportWin.initOwner( primaryStage );
reportWin.setScene( reportScene );
reportWin.setWidth( report.getWidth() );
reportWin.setHeight( report.getHeight() );
reportWin.setTitle( "now printing..." );
reportWin.show();
}catch( Exception e1 ){
e1.printStackTrace();
}
// 印刷ジョブの作成
PrinterJob job = PrinterJob.createPrinterJob();
if( job == null ){ return; }
// ジョブ状態を標準出力
job.jobStatusProperty().addListener( status -> System.out.println( "status : " + status ) );
// ジョブ名を設定
job.getJobSettings().setJobName( "領収書" );
// デフォルトプリンター(サイズ:A4横)でシーングラフを印刷
Printer printer = Printer.getDefaultPrinter();
PageLayout layout = printer.createPageLayout( Paper.A4 , PageOrientation.LANDSCAPE , Printer.MarginType.DEFAULT );
if( !job.printPage( layout , report ) ){ return; }
// 印刷ジョブの終了
if( !job.endJob() ){ return; }
// ウィンドウを閉じる
reportWin.close();
});
}
}
◇実行結果
(画面)
|
(印刷ボタン押下時に表示される画面)
|
|
(PDFに出力された内容。以下は、右回りに90度回転させた状態) |
◇解説
印刷ボタンを押下した場合のイベントハンドラにて、FXMLファイルの読込や印刷ジョブの作成を行っている(54行目~109行目)。57行目から72行目のFMXLの読込やコントローラによる値の代入の詳細に関しては
別記事などを参照いただきたい。今回は印刷時にプレビュー画面を出すというイメージで、帳票出力する内容をウィンドウ表示している(75行目~83行目)。
印刷ジョブについては設定ダイアログを利用せず、デフォルトプリンターでA4・横印刷を指定して印刷している(100行目~101行目)。印刷物を見ると印刷設定どおりに出力されるのがわかる。注意点としてはJavaFXアプリケーションスレッドで印刷ジョブを起動しないと、レイアウトが崩れてしまう点である。JavaDocでは別スレッドが推奨されている(
*2)が、実際の利用ではJavaFXアプリケーションスレッドでの印刷が良い場合もあるようである。
■ 参照
- SlideShare 「JavaOne2013報告会 JavaFX Update」
- JavaDoc 「クラスPrinterJob」